package com.zjt.smart.ui.base;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.OrientationHelper;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ImageView;

import com.zjt.smart.R;
import com.zjt.smart.api.server.BaseServer;
import com.zjt.smart.app.MainApplication;
import com.zjt.smart.mvp.model.Event;
import com.zjt.smart.ui.adapter.base.BaseAdapter;
import com.zjt.smart.ui.adapter.base.TypeAdapter;
import com.zjt.smart.ui.adapter.event.LayoutState;
import com.zjt.smart.ui.customview.RecycleViewDivider;
import com.zjt.smart.util.StatusUtil;
import com.zjt.smart.util.ToastUtil;

import butterknife.Bind;

/**
 * A Fragment that have a SwipeRefreshLayout(with layout id swipeRefreshLayout) and a RecyclerView(with layout id recyclerView)
 * NOTE: in subclass's XML file,those layout's is MUST be the same as above.
 */
public abstract class ScrollFragment extends StateFragment implements CanScroll {
    @Bind(R.id.swipeRefreshLayout)
    protected SwipeRefreshLayout mRefreshLayout;
    @Bind(R.id.recyclerView)
    protected RecyclerView mRecyclerView;
    protected RecyclerView.LayoutManager mRecyclerManager;
    protected BaseAdapter mAdapter;
    protected enum RecycleType {LIST,GRID,STAGGER}
    private boolean canLoadMore = true;
    private boolean canRefresh = true;
    private boolean move = false;
    private int mIndex = 0;

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mRefreshLayout.setEnabled(canRefresh());
        mRefreshLayout.setOnRefreshListener(this);
        mRefreshLayout.setColorSchemeResources(R.color.yellow,R.color.green);
        mAdapter = createAdapter();
        if (mAdapter == null) {
            throw new IllegalArgumentException("Adapter should not be null!");
        }
        if (canLoadMore()){
            mAdapter.setOnBottomListener(this);
        }else {
            mAdapter.setLoadState(LayoutState.GONE);
        }
        RecyclerView.ItemDecoration decoration = createItemDecoration();
        if (decoration != null) {
            mRecyclerView.addItemDecoration(decoration);
        }
        mRecyclerView.setAdapter(mAdapter);
        mRecyclerManager = createLayoutManager(mAdapter);
        mRecyclerView.setLayoutManager(mRecyclerManager);
        mRecyclerView.setItemAnimator(createItemAnimator());
        mRecyclerView.setOnScrollListener(new DefaultOnScrollListener());
    }

    protected void onScrollStateChange(RecyclerView recyclerView, int newState) {

    }

    protected void onScroll(RecyclerView recyclerView, int dx, int dy){

    }

    public void scrollTo(int position) {
        if (position < 0 || position > mAdapter.getItemCount()) {
            return;
        }
        mIndex = position;
        int firstItem = findFirstVisiblePosition();
        int lastItem = findLastVisiblePosition();
        if (position <= firstItem) {
            mRecyclerView.scrollToPosition(position);
            move = true;
        } else if (position <= lastItem) {
            int top = mRecyclerView.getChildAt(position - firstItem).getTop();
            mRecyclerView.scrollBy(0, top);
        } else {
            mRecyclerView.scrollToPosition(position);
            move = true;
        }
    }

    public void smoothScrollTo(int position) {
        if (position < 0 || position > mAdapter.getItemCount()) {
            return;
        }
        mIndex = position;
        int firstItem = findFirstVisiblePosition();
        int lastItem = findLastVisiblePosition();
        if (position <= firstItem) {
            mRecyclerView.smoothScrollToPosition(position);
            move = true;
        } else if (position <= lastItem) {
            int top = mRecyclerView.getChildAt(position - firstItem).getTop();
            mRecyclerView.smoothScrollBy(0, top);
        } else {
            mRecyclerView.smoothScrollToPosition(position);
            move = true;
        }

    }

    protected int findFirstVisiblePosition(){
        if (mRecyclerManager instanceof LinearLayoutManager) {
            return ((LinearLayoutManager) mRecyclerManager).findFirstVisibleItemPosition();
        }else if (mRecyclerManager instanceof GridLayoutManager){
            return ((GridLayoutManager) mRecyclerManager).findFirstVisibleItemPosition();
        }else if (mRecyclerManager instanceof StaggeredGridLayoutManager){
            int[] ids = null;
            ((StaggeredGridLayoutManager) mRecyclerManager).findFirstVisibleItemPositions(ids);
            int position = ids.length > 0? ids[0]:-1;
            return position;
        }else {
            return -1;
        }
    }

    protected int findLastVisiblePosition(){
        if (mRecyclerManager instanceof LinearLayoutManager) {
            return ((LinearLayoutManager) mRecyclerManager).findLastVisibleItemPosition();
        }else if (mRecyclerManager instanceof GridLayoutManager){
            return ((GridLayoutManager) mRecyclerManager).findLastVisibleItemPosition();
        }else if (mRecyclerManager instanceof StaggeredGridLayoutManager){
            int[] ids = null;
            ((StaggeredGridLayoutManager) mRecyclerManager).findLastVisibleItemPositions(ids);
            int position = ids.length > 0? ids[ids.length - 1]:-1;
            return position;
        }else {
            return -1;
        }
    }

    protected RecyclerView.LayoutManager createLayoutManager(BaseAdapter adapter){
        RecyclerView.LayoutManager manager;
        RecycleType type = getRecycleViewType();
        if (type == RecycleType.LIST) {
            manager = new LinearLayoutManager(getContext());
        } else if (type == RecycleType.GRID) {
            manager = new GridLayoutManager(getContext(), getSpanCount());
        } else {
            manager = new StaggeredGridLayoutManager(getSpanCount(), OrientationHelper.VERTICAL);
        }
        if (manager instanceof GridLayoutManager) {
            ((GridLayoutManager) manager).setSpanSizeLookup(new DefaultSpanSizeLookup(adapter));
        }
        if (manager instanceof StaggeredGridLayoutManager) {
            adapter.setLoadState(LayoutState.GONE);
        }
        return manager;
    }

    public void setCanRefresh(boolean canRefresh) {
        this.canRefresh = canRefresh;
        mRefreshLayout.setEnabled(canRefresh);
    }

    public void setCanLoadMore(boolean canLoadMore) {
        this.canLoadMore = canLoadMore;
        if (canLoadMore){
            mAdapter.setOnBottomListener(this);
        }else {
            mAdapter.setOnBottomListener(null);
            mAdapter.setLoadState(LayoutState.GONE);
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        unbindDrawables(mRecyclerView);
        mRecyclerView = null;
        if (mAdapter !=null){
            mAdapter.onDestroy();
            mAdapter = null;
        }
    }

    private void unbindDrawables(View view) {
        if (view == null){
            return;
        }
        if (view instanceof ImageView) {
            ((ImageView)view).setImageDrawable(null);
            if (view.getBackground()!=null){
                view.getBackground().setCallback(null);
            }
        }
        if (view instanceof ViewGroup && !(view instanceof AdapterView)) {
            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
                unbindDrawables(((ViewGroup) view).getChildAt(i));
            }
            ((ViewGroup) view).removeAllViews();
        }
    }

    protected void startRefresh(){
        mRefreshLayout.post(() -> {
            if (mRefreshLayout == null) return;
            mRefreshLayout.setRefreshing(true);
        });
        onRefresh();
    }


    @Override
    public void onRefresh() {
        mRefreshLayout.postDelayed(() -> {
            onRefreshDelayed();
        }, 1000);
        if (canLoadMore()) mAdapter.resetPage();
    }


    @Override
    public void onRefreshDelayed() { }

    /**
     * 需使用在mAdapter.addAll()之前
     */
    @Override
    protected void showContentView() {
        if (mRefreshLayout == null) return;
        super.showContentView();
        if (mRefreshLayout.isRefreshing()){
            if (mAdapter != null && mAdapter.getItemCount()>0){
                mAdapter.removeAll();
            }
            mRefreshLayout.setRefreshing(false);
        }
    }

    /**
     * 网络比缓存快则不显示CacheView
     */
    protected void showCacheView(){
        if (mRefreshLayout == null) return;
        if (mAdapter != null && mAdapter.getItemCount()>0){
            return;
        }
        super.showContentView();
    }

    protected void showErrorView(int errorCode){
        if (mRefreshLayout == null) return;
        if (mRefreshLayout.isRefreshing()){
            mRefreshLayout.setRefreshing(false);
        }
        if (errorCode == BaseServer.NO_MORE) {
            if (mAdapter.getItemCount() != 0){
                if (canLoadMore()) mAdapter.setLoadState(LayoutState.FINISHED);
            }else {
                super.showEmptyView();
            }
        }else {
            if (mAdapter.getItemCount() != 0){
                if (canLoadMore()) mAdapter.setLoadState(LayoutState.ERROR);
            }else {
                super.showErrorView();
            }
        }
        if (!StatusUtil.isNetworkAvailable(MainApplication.getApp())) {
            ToastUtil.showToast(R.string.no_net);
        }
    }


    protected BaseAdapter getAdapter(){
        return mAdapter;
    }

    //Call in Activity
    public void onToolbarClick() {
        mRecyclerView.smoothScrollToPosition(0);
    }

    @Override
    public boolean canRefresh() {
        return canRefresh;
    }

    public boolean canLoadMore() {
        return canLoadMore;
    }

    protected abstract BaseAdapter createAdapter();

    protected RecyclerView.ItemAnimator createItemAnimator() {return new DefaultItemAnimator();}

    protected RecyclerView.ItemDecoration createItemDecoration(){
        return new RecycleViewDivider(getContext(), RecycleViewDivider.Orientation.HORIZONTAL);
    }

    protected RecycleType getRecycleViewType() { return RecycleType.LIST; }

    protected int getSpanCount(){ return 1; }

    @Override
    public void onLoadMore(int toToLoadPage) {

    }

    @Override
    public void onEvent(Event event) {
        super.onEvent(event);
        if (mAdapter != null){
            mAdapter.notifyDataSetChanged();
        }
    }

    private class DefaultOnScrollListener extends RecyclerView.OnScrollListener{
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            onScrollStateChange(recyclerView, newState);
            if (move && newState == RecyclerView.SCROLL_STATE_IDLE ){
                move = false;
                int n = mIndex - findFirstVisiblePosition();
                if ( 0 <= n && n < mRecyclerView.getChildCount()){
                    int top = mRecyclerView.getChildAt(n).getTop();
                    mRecyclerView.smoothScrollBy(0, top);
                }
            }
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            onScroll(recyclerView, dx, dy);
            if (move){
                move = false;
                int n = mIndex - findFirstVisiblePosition();
                if ( 0 <= n && n < mRecyclerView.getChildCount()){
                    int top = mRecyclerView.getChildAt(n).getTop();
                    mRecyclerView.scrollBy(0, top);
                }
            }
        }
    }

    private class DefaultSpanSizeLookup extends GridLayoutManager.SpanSizeLookup{
        private BaseAdapter adapter;
        public DefaultSpanSizeLookup(BaseAdapter adapter){
            this.adapter = adapter;
        }

        @Override
        public int getSpanSize(int position) {
            if (adapter == null) return 0;
            if (position == adapter.getItemCount() - 1) {
                return getSpanCount();
            }
            if (adapter instanceof TypeAdapter)
                return ((TypeAdapter) adapter).getSpanCount(position);
            return 1;
        }
    }

}
